package esim.service.supports;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.power.common.util.DateTimeUtil;
import esim.service.domain.Order;
import esim.service.entity.FPInfo;
import esim.service.entity.InvoiceInfo;
import esim.service.entity.OrderSnapshot;
import esim.service.enums.Constant;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import sajt.shdzfp.bean.*;
import sajt.shdzfp.kp.InvManagerService;
import sun.misc.BASE64Decoder;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @Author: wbb
 * @Date: 2021/4/19 16:43
 */
@Slf4j
public class ShdzfpUtils {
    private final static InvManagerService INV_MANAGER_SERVICE = new InvManagerService();
    private final static ObjectMapper MAPPER = new XmlMapper();

    public static int issueInvoice(List<Order> orders, InvoiceInfo invoice, ShdzfpProperty property, String invoiceNo) {
        GlobalInfo globalInfo = createGlobalInfo(property, invoiceNo, "ECXML.FPKJ.BC.E_INV");
        ReturnStateInfo info = new ReturnStateInfo();
        FPKJXX_XMXX[] xmxxes = transOrder(orders);
        BigDecimal xmje = Arrays.stream(xmxxes).map(fpkjxx_xmxx -> new BigDecimal(fpkjxx_xmxx.getXMJE())).reduce(BigDecimal.ZERO, BigDecimal::add);

        //todo：计算counts 获取开票项目(xmxxes[0].XMMC) 根据订单计算开票合计金额，不含税金额，含税金额
        String[] counts = new String[]{"充值", xmje.toString(), "", ""};

        REQUEST_FPKJXX fpkjxx = getRequestFpkjxx(property, invoiceNo, invoice, counts);
        fpkjxx.setFPKJXX_XMXXS(xmxxes);
        Data data = createData();
        String res;
        try {
            /*
              3DES加密时，key必传,CaMsg传null;CA加密时，key传null，CaMsg必传
              布尔值为是否为生产环境，false：测试环境  true：生产环境
             */
            res = INV_MANAGER_SERVICE.issueInv(data, info, globalInfo, fpkjxx, property.getEncryptKey(), null, property.getServiceUrl(), property.getProdEnable());
            log.info("开具发票返回报文:{}", res);
        } catch (Exception e) {
            log.error("开具发票失败", e);
            throw new EsimException(e.getMessage(), HttpStatus.BAD_REQUEST.value());
        }
        ReturnStateInfo returnStateInfo = transReturnStateInfo(res);
        if (Constant.E9616CODE.equals(returnStateInfo.getReturnCode())) {
            return 1;
        }
        if (!isRequestSuccess(returnStateInfo)) {
            log.error("开具发票接口调用失败:{}", getReturnMsg(returnStateInfo));
            assert returnStateInfo != null;
            throw new EsimException(getReturnMsg(returnStateInfo), Integer.parseInt(returnStateInfo.getReturnCode()));
        }
        return 0;
    }

    public static String downloadInvoice(ShdzfpProperty property, String dataExchangeId, String invoiceNo) {
        GlobalInfo globalInfo = createGlobalInfo(property, dataExchangeId, "ECXML.FPXZ.CX.E_INV");
        ReturnStateInfo info = new ReturnStateInfo();
        Data data = createData();
        REQUEST_FPXXXZ_NEW fpxxxz = new REQUEST_FPXXXZ_NEW();
        fpxxxz.setDSPTBM(property.getUserName());
        fpxxxz.setFPQQLSH(invoiceNo);
        fpxxxz.setNSRSBH(property.getTaxpayerId());
        fpxxxz.setPDF_XZFS("2");
        fpxxxz.setDDH(invoiceNo);
        String res;
        try {
            res = new InvManagerService().loadInv(data, info, globalInfo, fpxxxz, property.getEncryptKey(), null, property.getServiceUrl(), property.getProdEnable());
            log.info("下载发票返回报文:{}", res);
        } catch (Exception e) {
            log.error("下载发票失败", e);
            throw new RuntimeException(e);
        }
        ReturnStateInfo returnStateInfo = transReturnStateInfo(res);
        if (isRequestSuccess(returnStateInfo)) {
            return loadPdfUrl(res);
        } else {
            log.error("下载发票接口调用失败:{}", getReturnMsg(returnStateInfo));
            return null;
        }
    }

    public static GlobalInfo createGlobalInfo(ShdzfpProperty property, String invoiceNo, String interfaceCode) {
        GlobalInfo globalInfo = new GlobalInfo();
        globalInfo.setTerminalCode("0");
        globalInfo.setAppId("ZZS_PT_DZFP");
        globalInfo.setVersion("1");
        globalInfo.setInterfaceCode(interfaceCode);
        globalInfo.setRequestCode(property.getUserName());
        String time = DateTime.now().toString("yyyy-MM-dd HH:mm:ss");
        globalInfo.setRequestTime(time);
        globalInfo.setRequestCode("121");
        globalInfo.setDataExchangeId(invoiceNo);
        globalInfo.setUserName(property.getUserName());
        globalInfo.setPassWord("");
        globalInfo.setTaxpayerId(property.getTaxpayerId());
        globalInfo.setAuthorizationCode(property.getAuthorizationCode());
        return globalInfo;
    }

    /**
     * @param counts [KPXM, KPHJJE, HJBHSJE, HJSE]
     * @return REQUEST_FPKJXX
     */
    private static REQUEST_FPKJXX getRequestFpkjxx(ShdzfpProperty property, String invoiceNo, InvoiceInfo invoice, String[] counts) {
        REQUEST_FPKJXX fpkjxx = new REQUEST_FPKJXX();
        FPKJXX_FPTXX fptxx = new FPKJXX_FPTXX();
        fptxx.setFPQQLSH(invoiceNo);
        fptxx.setDSPTBM(property.getUserName());
        fptxx.setNSRSBH(property.getTaxpayerId());
        fptxx.setNSRMC(new String(property.getNsrmc().getBytes(ISO_8859_1), UTF_8));  //开票方名称  开票金税盘的销方纳税人全称，由企业提供，测试环境由平台提供
        fptxx.setDKBZ("0");
        fptxx.setKPXM(counts[0]);
        fptxx.setBMB_BBH("50.0");  //编码表版本号
        fptxx.setXHF_NSRSBH(property.getTaxpayerId());
        fptxx.setXHFMC(new String(property.getNsrmc().getBytes(ISO_8859_1), UTF_8));
        fptxx.setXHF_YHZH(new String(property.getAccount().getBytes(ISO_8859_1), UTF_8));
        fptxx.setXHF_DZ(new String(property.getAddress().getBytes(ISO_8859_1), UTF_8));
        fptxx.setXHF_DH(new String(property.getPhone().getBytes(ISO_8859_1), UTF_8));
        fptxx.setGHF_DZ(invoice.getGhfDZ());
        fptxx.setGHF_EMAIL(invoice.getEmail());
        fptxx.setGHF_GDDH(invoice.getGhfGDDH());  //购货方固定电话
        fptxx.setGHF_NSRSBH(invoice.getTaxpayerNumber());
        fptxx.setGHF_SJ(invoice.getPhone());
        fptxx.setGHF_YHZH(invoice.getGhfYH()+invoice.getGhfZH());
        fptxx.setGHFMC(invoice.getTitle());
        fptxx.setGHFQYLX(invoice.getOrgType());
        fptxx.setKPY(new String(property.getAccessor().getBytes(ISO_8859_1), UTF_8));
        fptxx.setSKY(new String(property.getSky().getBytes(ISO_8859_1), UTF_8));  //收款人
        fptxx.setFHR(new String(property.getFhr().getBytes(ISO_8859_1), UTF_8));  //复核人
        fptxx.setKPLX("1"); //开票类型 //1：正票，2：红票
        fptxx.setYFP_DM("");
        fptxx.setYFP_HM("");
        fptxx.setCZDM("10");   //操作代码 10：正票正常开具，20：退货折让红票
        fptxx.setQD_BZ("0");   //默认为0(商品明细大于8行，平台自动生成清单)。

        fptxx.setKPHJJE(counts[1]);  //价税合计金额
        fptxx.setHJBHSJE(counts[2]);  //合计不含税金额。所有商品行不含税金额之和。
        fptxx.setHJSE(counts[3]);  //合计税额。所有商品行税额之和。

        fptxx.setBZ("");
        fptxx.setBYZD1("");
        fptxx.setBYZD2("");
        fptxx.setBYZD3("");
        fptxx.setBYZD4("");
        fptxx.setBYZD5("");
        fptxx.setFJH("");
        fptxx.setKPRQ(DateTimeUtil.dateToStr(new Date(),"yyyy-MM-dd HH:mm:ss"));
        fpkjxx.setFPKJXX_FPTXX(fptxx);

        FPKJXX_DDXX ddxx = new FPKJXX_DDXX();
        ddxx.setDDDATE("");
        ddxx.setDDH(invoiceNo);
        fpkjxx.setFPKJXX_DDXX(ddxx);
        return fpkjxx;
    }

    private static FPKJXX_XMXX[] transOrder(List<Order> orderList) {
        FPKJXX_XMXX[] xmxxes = new FPKJXX_XMXX[orderList.size()];
        for (int i = 0; i < orderList.size(); i++) {
            Order order = orderList.get(i);
            //todo:按照API文档将订单信息转换为 FPKJXX_XMXX对象

            //snapshot中包含商品当时的名称，用作项目名称， snapshot中包含商品当时的单价
            OrderSnapshot snapshot = JSON.parseObject(order.getOrderSnapshot(), OrderSnapshot.class);

            String orderAmount = order.getOrderAmount().toString();

            FPKJXX_XMXX xmxx = new FPKJXX_XMXX();
            xmxx.setXMMC("充值");
            xmxx.setXMDW("");
            xmxx.setGGXH("");
            xmxx.setXMSL("1");  //项目数量
            xmxx.setHSBZ("1");  //含税标志 表示项目单价和项目金额是否含税。0表示都不含税，1表示都含税
            xmxx.setFPHXZ("0");  //发票行性质
            xmxx.setXMDJ(orderAmount);  //项目单价
            xmxx.setSPBM("3040101");   //商品编码
            xmxx.setZXBM("");
            xmxx.setYHZCBS("0");  //优惠政策标识
            xmxx.setLSLBS("");
            xmxx.setZZSTSGL("");
            xmxx.setKCE("0");  //扣除额
            xmxx.setXMJE(orderAmount);  //项目金额
            xmxx.setSL("0.06");  //税率
            xmxx.setSE("0");  //税额
            xmxx.setBYZD1("");
            xmxx.setBYZD2("");
            xmxx.setBYZD3("");
            xmxx.setBYZD4("");
            xmxx.setBYZD5("");
            xmxxes[i] = xmxx;
        }
        return xmxxes;
    }

    public static Data createData() {
        Data data = new Data();
        //设置加密方式，是否压缩
        data.setZipCode("1");
        data.setEncryptCode("1");
        data.setCodeType("3DES加密");
        return data;
    }

    public static ReturnStateInfo createReturnStateInfo(String returnCode, String returnMessage){
        ReturnStateInfo returnStateInfo = new ReturnStateInfo();
        returnStateInfo.setReturnCode(returnCode);
        returnStateInfo.setReturnMessage(returnMessage);
        return returnStateInfo;
    }

    private static ReturnStateInfo transReturnStateInfo(String res) {
        int begin = res.indexOf("<returnStateInfo>");
        if (begin < 0) {
            throw new RuntimeException("接口调用失败，状态报文不完整");
        }
        int end = res.indexOf("</returnStateInfo>");
        if (end < 0) {
            throw new RuntimeException("接口调用失败，状态报文不完整");
        }
        String stateStr = res.substring(begin, end + 18);
        try {
            return MAPPER.readValue(stateStr, ReturnStateInfo.class);
        } catch (JsonProcessingException e) {
            log.info("解析发票返回结果出现错误", e);
            return null;
        }
    }

    private static boolean isRequestSuccess(ReturnStateInfo info) {
        if (info == null) {
            return false;
        }
        int code = Integer.parseInt(info.getReturnCode());
        return code == 0;
    }

    private static String getReturnMsg(ReturnStateInfo info) {
        if (info == null) {
            return "无明确结果";
        }
        String message = info.getReturnMessage();
        if (StringUtils.isEmpty(message)) {
            return "";
        }
        try {
            byte[] bytes = new BASE64Decoder().decodeBuffer(message);
            return new String(bytes);
        } catch (IOException e) {
            log.info("返回信息Base64解码失败");
            return "";
        }
    }

    public static String loadPdfUrl(String res) {
        int begin = res.indexOf("<REQUEST_FPKJXX_FPJGXX_NEW");
        if (begin < 0) {
            throw new RuntimeException("接口调用失败，状态报文不完整");
        }
        int end = res.indexOf("</REQUEST_FPKJXX_FPJGXX_NEW>");
        if (end < 0) {
            throw new RuntimeException("接口调用失败，状态报文不完整");
        }
        String content = res.substring(begin, end + 28);
        try {
            TypeReference<Map<String, String>> reference = new TypeReference<Map<String, String>>() {
            };
            Map<String, String> map = MAPPER.readValue(content, reference);
            final String key = "PDF_URL";
            return map.get(key);
        } catch (JsonProcessingException e) {
            log.error("解析返回报文结构失败", e);
            return null;
        }
    }

    public static boolean sendMail(ShdzfpProperty property, String invoiceNo, String emailAddress,
                                  String fpDm, String fpHm) {
        Data data = createData();
        ReturnStateInfo info = new ReturnStateInfo();
        GlobalInfo globalInfo = createGlobalInfo(property, invoiceNo, "ECXML.EMAILPHONEFPTS.TS.E.INV");
        REQUEST_EMAILPHONEFPTS request_emailphonefpts = new REQUEST_EMAILPHONEFPTS();

        TSFSXX tsfsxx = new TSFSXX();
        COMMON_NODE[] common_nodes = new COMMON_NODE[3];
        COMMON_NODE tsfs = new COMMON_NODE();
        tsfs.setNAME("TSFS");
        tsfs.setVALUE("0");   //0-邮箱推送，若填写了正确的手机号码，手机端也会推送

        COMMON_NODE sj = new COMMON_NODE();
        sj.setNAME("SJ");
        sj.setVALUE("");  //手机

        COMMON_NODE email = new COMMON_NODE();
        email.setNAME("EMAIL");
        email.setVALUE(emailAddress);   //邮箱

        common_nodes[0] = tsfs;
        common_nodes[1] = sj;
        common_nodes[2] = email;

        tsfsxx.setCOMMON_NODES(common_nodes);

        FPXX[] fpxxs = new FPXX[1];
        FPXX fpxx = new FPXX();

        COMMON_NODE[] common_nodes1 = new COMMON_NODE[4];
        //发票请求唯一流水号
        COMMON_NODE FPQQLSH = new COMMON_NODE();
        FPQQLSH.setNAME("FPQQLSH");
        FPQQLSH.setVALUE(invoiceNo);

        //开票方识别号
        COMMON_NODE NSRSBH = new COMMON_NODE();
        NSRSBH.setNAME("NSRSBH");
        NSRSBH.setVALUE(property.getTaxpayerId());

        //发票代码
        COMMON_NODE FP_DM = new COMMON_NODE();
        FP_DM.setNAME("FP_DM");
        FP_DM.setVALUE(fpDm);

        //发票号码
        COMMON_NODE FP_HM = new COMMON_NODE();
        FP_HM.setNAME("FP_HM");
        FP_HM.setVALUE(fpHm);
        common_nodes1[0] = FPQQLSH;
        common_nodes1[1] = NSRSBH;
        common_nodes1[2] = FP_DM;
        common_nodes1[3] = FP_HM;

        fpxx.setCOMMON_NODES(common_nodes1);

        fpxxs[0]=fpxx;

        request_emailphonefpts.setTSFSXX(tsfsxx);
        request_emailphonefpts.setFPXXS(fpxxs);
        String res;
        try {
            res = new InvManagerService().doEmail(data, info, globalInfo, request_emailphonefpts, property.getEncryptKey(), null, property.getServiceUrl(), property.getProdEnable());
            log.info("发票发送邮箱返回报文:{}", res);
        } catch (Exception e) {
            log.error("发票发送邮箱失败", e);
            throw new EsimException(e.getMessage(),HttpStatus.BAD_REQUEST.value());
        }
        ReturnStateInfo returnStateInfo = transReturnStateInfo(res);
        if (!isRequestSuccess(returnStateInfo)) {
            log.error("发票发送邮箱失败:{}", getReturnMsg(returnStateInfo));
            assert returnStateInfo != null;
            throw new EsimException(getReturnMsg(returnStateInfo),Integer.parseInt(returnStateInfo.getReturnCode()));
        }

        return true;

    }
    //发票信息获取(包含明细，文件类型)
    public static FPInfo.Data.RESPONSE_FPMXXZ invoiceDetail(ShdzfpProperty property, String invoiceNo) {

        Data data = createData();
        ReturnStateInfo info = new ReturnStateInfo();
        GlobalInfo globalInfo = createGlobalInfo(property, invoiceNo, "ECXML.FPMXXZ.CX.E_INV");
        REQUEST_FPXXXZ_NEW request_fpxxxz_new = new REQUEST_FPXXXZ_NEW();
        request_fpxxxz_new.setFPQQLSH(invoiceNo);
        request_fpxxxz_new.setDSPTBM(property.getUserName());
        request_fpxxxz_new.setNSRSBH(property.getTaxpayerId());
        request_fpxxxz_new.setDDH("");
        request_fpxxxz_new.setPDF_XZFS("2");
        String res;
        try {
            res = new InvManagerService().loadInv(data, info, globalInfo, request_fpxxxz_new, property.getEncryptKey(), null, property.getServiceUrl(), property.getProdEnable());
            log.info("发票明细接口返回报文:{}", res);
        } catch (Exception e) {
            log.error("发票明细接口失败", e);
            throw new RuntimeException(e);
        }
        ReturnStateInfo returnStateInfo = transReturnStateInfo(res);
        if (isRequestSuccess(returnStateInfo)) {
            log.info("发票明细接口调用成功:{}", getReturnMsg(returnStateInfo));
            Object source = convertXmlStrToObject(FPInfo.class, res);
            FPInfo target = new FPInfo();
            BeanUtils.copyProperties(source,target);
            return target.getData().getContent().getRESPONSE_FPMXXZ();
        } else {
            assert returnStateInfo != null;
            String returnMsg = getReturnMsg(returnStateInfo);
            String returnCode = returnStateInfo.getReturnCode();
            if(Constant.E9410CODE.equals(returnCode)){
                returnMsg= Constant.E9410MSG;
            }
            log.error("发票明细接口调用失败:{}", returnMsg);
            throw new EsimException(returnMsg,Integer.parseInt(returnCode));
        }
    }

    public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader sr = new StringReader(xmlStr);
            xmlObject = unmarshaller.unmarshal(sr);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }
}
